/*
 * Figure.cpp
 *
 *  Created on: 28/03/2013
 *      Author: gonzalo
 */

#include "Figure.h"

#include <cstdio>

Figure::Figure(GLuint program) {
	this->view_matrix = NULL;
	this->model_matrix = glm::mat4(1.0f);
	this->vertex_buffer = NULL;
	this->index_buffer = NULL;
	this->normal_buffer = NULL;
	this->texture_buffer = NULL;
	this->tangent_buffer = NULL;

	this->vertex_buffer_size = 0;
	this->index_buffer_size = 0;
	this->normal_buffer_size = 0;
	this->texture_buffer_size = 0;
	this->tangent_buffer_size = 0;

	this->position = glm::vec3(0.0, 0.0, 0.0);
	this->show_normals = false;
	this->with_texture = false;
}

Figure::~Figure() {
}


void Figure::traslate(glm::vec3 const & traslation) {
	this->position += traslation;
	this->model_matrix = glm::translate(this->model_matrix, traslation);
}

void Figure::rotate(GLfloat const & angle, glm::vec3 const & rotation_vector) {
	this->model_matrix = glm::rotate(this->model_matrix, angle, rotation_vector);
}

void Figure::scale(glm::vec3 const & scale_factor) {
	this->model_matrix = glm::scale(this->model_matrix, scale_factor);
}

void Figure::set_model_matrix(glm::mat4 const & matrix) {
	this->model_matrix = matrix;
}

void Figure::set_vertex_coords(GLuint vertex_num, GLfloat x, GLfloat y, GLfloat z) {
	GLuint index = vertex_num * 3;
	this->vertex_buffer[index] = x;
	this->vertex_buffer[index + 1] = y;
	this->vertex_buffer[index + 2] = z;
}

void Figure::set_coords3(GLfloat * buffer, GLuint vertex_num, GLfloat x, GLfloat y, GLfloat z) {
	GLuint index = vertex_num * 3;
	buffer[index] = x;
	buffer[index + 1] = y;
	buffer[index + 2] = z;
}

void Figure::set_coords2(GLfloat * buffer, GLuint vertex_num, GLfloat x, GLfloat y) {
	GLuint index = vertex_num * 2;
	buffer[index] = x;
	buffer[index + 1] = y;
}

glm::vec3 Figure::get_position() {
	return this->position;
}

glm::vec3 Figure::get_transformated_origin() {
	glm::vec4 origin = glm::vec4(0.0f);
	glm::vec3 transformated_origin;

	origin.w = 1.0;

	origin = this->model_matrix * origin;

	transformated_origin.x = origin.x;
	transformated_origin.y = origin.y;
	transformated_origin.z = origin.z;

	return transformated_origin;
}

glm::vec3 Figure::get_transformated_origin(glm::mat4 & matriz_vista) {
	glm::vec4 origin = glm::vec4(0.0f);
	glm::vec3 transformated_origin;

	origin.w = 1.0;

	origin = matriz_vista * this->model_matrix * origin;

	transformated_origin.x = origin.x;
	transformated_origin.y = origin.y;
	transformated_origin.z = origin.z;

	return transformated_origin;
}

void Figure::set_type(GLuint type) {
	if (type == GL_TRIANGLES || type == GL_QUADS) {
		this->type = type;
	} else {
		this->type = GL_TRIANGLES;
	}
}

void Figure::draw_normals() {
	GLfloat * normales;
	GLfloat altura = 0.1;
	GLuint index;
	GLuint _index_buffer;
	normales = new GLfloat[3 * 2 * index_buffer_size];

	glDisableClientState(GL_NORMAL_ARRAY);

	GLfloat currentColor[4];
	glGetFloatv(GL_CURRENT_COLOR, currentColor);

	glColor3f(1.0, 1.0, 1.0);

	index = 0;
	for (uint i = 0; i < index_buffer_size; i++) {
		index = 6 * i;
		_index_buffer = 3 * index_buffer[i];
		normales[index] = vertex_buffer[_index_buffer];
		normales[index + 1] = vertex_buffer[_index_buffer + 1];
		normales[index + 2] = vertex_buffer[_index_buffer + 2];
		normales[index + 3] = altura * normal_buffer[3 * i] + vertex_buffer[_index_buffer];
		normales[index + 4] = altura * normal_buffer[3 * i + 1] + vertex_buffer[_index_buffer + 1];
		normales[index + 5] = altura * normal_buffer[3 * i + 2] + vertex_buffer[_index_buffer + 2];
	}

	glVertexPointer(3, GL_FLOAT, 0, normales);
	glDrawArrays(GL_LINES, 0, 2 * index_buffer_size);
	glEnableClientState(GL_NORMAL_ARRAY);
	glColor3f(currentColor[0], currentColor[1], currentColor[2]);
	delete[] normales;
}

void Figure::draw_normals(GLfloat * normals, GLuint * index, GLuint index_size) {
	GLfloat * normales;
	GLfloat altura = 0.1;
	GLuint _index;
	GLuint _index_buffer;
	normales = new GLfloat[3 * 2 * index_size];

	glDisableClientState(GL_NORMAL_ARRAY);

	GLfloat currentColor[4];
	glGetFloatv(GL_CURRENT_COLOR, currentColor);

	glColor3f(1.0, 1.0, 1.0);

	_index = 0;
	for (uint i = 0; i < index_size; i++) {
		_index = 6 * i;
		_index_buffer = 3 * index[i];
		normales[_index] = vertex_buffer[_index_buffer];
		normales[_index + 1] = vertex_buffer[_index_buffer + 1];
		normales[_index + 2] = vertex_buffer[_index_buffer + 2];
		normales[_index + 3] = altura * normals[3 * i] + vertex_buffer[_index_buffer];
		normales[_index + 4] = altura * normals[3 * i + 1] + vertex_buffer[_index_buffer + 1];
		normales[_index + 5] = altura * normals[3 * i + 2] + vertex_buffer[_index_buffer + 2];
	}

	glVertexPointer(3, GL_FLOAT, 0, normales);
	glDrawArrays(GL_LINES, 0, 2 * index_size);
	glEnableClientState(GL_NORMAL_ARRAY);
	glColor3f(currentColor[0], currentColor[1], currentColor[2]);
	delete[] normales;
}

void Figure::set_show_normals(bool value) {
	this->show_normals = true;
}

void Figure::calculate_and_bind_normal_matrix(GLuint  program,const glm::mat4 & matriz) {
	// Normal Matrix
	glm::mat3 normal_matrix = glm::mat3(1.0f);
	for (int i = 0; i < 3; i++)
		for (int j = 0; j < 3; j++)
			normal_matrix[i][j] = matriz[i][j];

	// Calculo la inversa transpuesta
	normal_matrix = glm::inverse(normal_matrix);
	normal_matrix = glm::transpose(normal_matrix);

	// Bind Normal Matrix
	GLuint location_normal_matrix = glGetUniformLocation(program, "NormalMatrix");
	if (location_normal_matrix >= 0) {
		glUniformMatrix3fv(location_normal_matrix, 1, GL_FALSE, &normal_matrix[0][0]);
	}

}

void Figure::bind_model_matrix(GLuint  program,const glm::mat4 & matriz) {
	// Bind Model Matrix
	GLuint location_world_matrix = glGetUniformLocation(program, "ModelMatrix");
	if (location_world_matrix >= 0) {
		glUniformMatrix4fv(location_world_matrix, 1, GL_FALSE, &matriz[0][0]);
	}
}

void Figure::load_texture(const char* filename) {
	// Load texture file
	text_image_buffer = SOIL_load_image(filename, &text_image_witdh, &text_image_height, &text_image_channels,
			SOIL_LOAD_RGBA);
}

void Figure::activate_texture(GLuint  program) {
	// Copy file to OpenGL
	glUseProgram(program);
	GLint result;
	glGetProgramiv(program, GL_LINK_STATUS, &result);
	if (result == GL_FALSE) {
		fprintf(stderr, "Error al linkear");
	}
	GLuint textureid;
	glActiveTexture(GL_TEXTURE0);
	glGenTextures(1, &textureid);
	glBindTexture(GL_TEXTURE_2D, textureid);

	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, text_image_witdh, text_image_height, 0, GL_RGBA, GL_UNSIGNED_BYTE,
			text_image_buffer);
	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

	// Set the Tex1 sampler uniform to refer to texture unit 0
	int loc = glGetUniformLocation(program, "Tex1");

	if (loc != -1) {
		// We indicate that Uniform Variable sampler2D "text" uses  Texture Unit 0
		glUniform1i(loc, 0);
	} else {
		fprintf(stderr, "No puedo asignar Tex1 en programa!\n");
	}
}

void Figure::bind_tangent_buffer(GLuint  program,const char * buffer_name) {

	// GLint glGetAttribLocation(GLuint program,char *name);

	if (this->tangent_buffer == NULL) {
		return;
	}

	GLint loc = glGetAttribLocation(program, buffer_name);
	if (loc >= 0) {
		glEnableVertexAttribArray(loc);
		//void glVertexAttribPointer(GLint loc, GLint size, GLenum type, GLboolean normalized, GLsizei stride,
		//		const void * pointer);
		glVertexAttribPointer(loc, 3, GL_FLOAT, false, 0, tangent_buffer);
		// We indicate that Uniform Variable sampler2D "text" uses  Texture Unit 0
		//glVertexAttrib3fv(loc, this->tangent_buffer);
		//void glEnableVertexAttribArray(GLint loc);
	}

}

void Figure::set_texture_id(GLuint id) {

	this->with_texture = true;

	this->texture = id;
}

void Figure::change_texture_data(const std::vector<DatosVertice> & texturas){

}
